package testApplets;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import marchingCubes.MCubeEngineLowMemory;
import marchingCubes.OctCell;

import peasy.PeasyCam;
import processing.core.PApplet;
import simpleGeom.Bounds3D;
import simpleGeom.Box3D;
import simpleGeom.Face3D;
import simpleGeom.IsNode;
import simpleGeom.Node;
import simpleGeom.SimpleObjLoaderStream;
import simpleGeom.TrianglePreCalc;
import simpleGeom.Vec3D;

public class OctTreeTest extends PApplet {
	ArrayList<Node> nodes = new ArrayList<Node>();
	ArrayList<Face3D> faces = new ArrayList<Face3D>();
	String folderPath = "/Users/caad/Desktop/Archive/";

	String pathObjIn = folderPath + "form | 11-09-12 | 14'28'40 |.obj";
	String pathObjOut = folderPath + "formHighDetail.obj";
	MCubeEngineLowMemory mm;
	OctCell rootCell;
	Bounds3D b;
	PeasyCam cam;
	Vector<Face3D>mcFaces;
	float iso;
	float scalefactor=1;
	boolean doSave;
	public void setup() {
		size(1600,900,P3D);
		//loadObj("/Users/administrator/Desktop/testCyl.obj");
		loadObj(pathObjIn);
		for (int i=0;i<faces.size();i++){
			TrianglePreCalc f=(TrianglePreCalc) faces.get(i);
			f.init();
		}
		println("loaded");
		
		b=new Bounds3D();
		b.addAll(nodes);
		Box3D ob=b.getOffsetBox(b.getMaxDimension()/50f);
		float maxD=ob.getMaxDimension();
		scalefactor=1000f/maxD;
		rootCell=new OctCell(ob.x1,ob.y1,ob.z1,ob.x1+maxD,ob.y1+maxD,ob.z1+maxD);
		rootCell.maxgeneration=11;
		rootCell.minD=ob.getMaxDimension()/2048f;
		iso=rootCell.minD*0.9f;
		
		for (int i=0;i<faces.size();i++){
			TrianglePreCalc f=(TrianglePreCalc)faces.get(i);
			//f.offset=iso+rootCell.minD*1.7f;
			f.init();
		}
		
		for (int i=0;i<faces.size();i++){
			TrianglePreCalc f=(TrianglePreCalc)faces.get(i);
			rootCell.insert(f.getBounds3D(),f);
			if (i%100000==0)println(i+"/"+faces.size());
		}
		//rootCell.insertAll((List)faces);
		println("inserted");
		cam=new PeasyCam(this,b.getCenterX()*scalefactor,b.getCenterY()*scalefactor,b.getCenterZ()*scalefactor,b.getMaxDimension()*scalefactor);
		
		
		Vector<OctCell>cells=new Vector<OctCell>();
		rootCell.collectAllEndChildren(cells);
		/*for (int i=0;i<cells.size();i++){
			OctCell cell=cells.get(i);
			float cX=cell.getCenterX();
			float cY=cell.getCenterY();
			float cZ=cell.getCenterZ();
			float minD=10f;
			if (cell.objects!=null){
			for (int j=0;j<cell.objects.size();j++){
				TrianglePreCalc f3d=(TrianglePreCalc) cell.objects.get(j);
				float d=f3d.getDistance(cX,cY,cZ);
				if (d<minD){
					minD=d;
				}
			}
		
			cell.value=minD;
			}
			if (i%10000==0){
				println(i+"of" +cells.size());
				println(cell.minD);
				println(cell.getMinDimension());
			}
		}*/
		mm=new MCubeEngineLowMemory();
		mcFaces=mm.initCubesOct(rootCell, iso);
		saveObj(pathObjOut);
		println("saved");
	}
	
	public void loadObj(String path){
		SimpleObjLoaderStream stream = new SimpleObjLoaderStream();
		stream.init(path);
		float[] vertex = stream.readVertex();
		while (vertex != null) {
			nodes.add(new Node(vertex[0], vertex[1], vertex[2]));
			vertex = stream.readVertex();
		}
		
		stream.close();
		stream.init(path);
		int[] face = stream.readFace();
		while (face != null) {
			Face3D f = new Face3D();
			if (face.length == 4) {
				f = new TrianglePreCalc(nodes.get(face[0] - 1),
						nodes.get(face[1] - 1), nodes.get(face[2] - 1));
				faces.add(f);
				f = new TrianglePreCalc(nodes.get(face[2] - 1),
						nodes.get(face[3] - 1), nodes.get(face[0] - 1));
				faces.add(f);
	
				for (int i = 0; i < face.length; i++) {
					Node n = nodes.get(face[i] - 1);
					n.setComment(1);
				}
			} else {
				f = new TrianglePreCalc(nodes.get(face[0] - 1),
						nodes.get(face[1] - 1), nodes.get(face[2] - 1));
				for (int i = 0; i < face.length; i++) {
					Node n = nodes.get(face[i] - 1);
					n.setComment(1);
				}
				faces.add(f);
			}
			face = stream.readFace();
		}
	}

	public void draw(){
		if (doSave) {
			println("save");
			saveObj(this.selectOutput());
			doSave = false;
		}
		background(0,0,0);
		scale(scalefactor);
		lights();
		
		stroke(0,0,255);
		//noStroke();
		fill(255,0,0);
		for (int i=0;i<faces.size();i++){
			Face3D f=faces.get(i);
			f.display3D(g);
		}
		fill(255);
		//noFill();
		for (int i=0;i<mcFaces.size();i++){
			Face3D f=mcFaces.get(i);
			f.display3D(g);
		}
		Vector<OctCell>cells=new Vector<OctCell>();
		rootCell.collectAllEndChildren(cells);
		noFill();
		stroke(255,0,255);
		sphereDetail(6);
		for (int i=0;i<cells.size();i++){
			OctCell cell=cells.get(i);
			//cell.display3D(g);
			
			/*pushMatrix();
			translate(cell.getCenterX(),cell.getCenterY(),cell.getCenterZ());
			sphere(iso);
			popMatrix();*/
			//point(cell.x1,cell.y1,cell.z1);
		}
		;
		/*for (int i=0;i<mm.edges.size();i++){
			MCEdge e=mm.edges.get(i);
			stroke(0,255,255);
			line(e.v1.getX(),e.v1.getY(),e.v1.getZ(),e.v2.getX(),e.v2.getY(),e.v2.getZ());
			if (e.m!=null){
				
				stroke(0,255,0);
				pushMatrix();
				translate(e.m.x,e.m.y,e.m.z);
				box(0.01f);
				popMatrix();
			}
		}*/
	}
	public float getRound(float r,int n){
		return Math.round(r*n)/n;
	}
	
	public void saveObj(String file){
		int idV=1;
		FileOutputStream fileOutputStream = null;
		PrintStream printstream = null;
		try {
			fileOutputStream = new FileOutputStream(file);
			printstream = new PrintStream(fileOutputStream);
		} catch (Exception e) {
			System.err.println("error writing file");
		}
		for (int i=0;i<mcFaces.size();i++){
			Face3D f=mcFaces.get(i);
			for (int j=0;j<f.getNNodes();j++){
				Vec3D n=(Vec3D) f.getNode(j);
				if (n.getId()==0){
					n.setId(idV);
					idV++;
					String s="v "+n.x+" "+n.y+" "+n.z; 
					printstream.println(s);
				}
			}
		}
		for (int i=0;i<mcFaces.size();i++){
			Face3D f=mcFaces.get(i);
			String faceString="f";
			for (int j=0;j<f.getNNodes();j++){
				Vec3D n=(Vec3D) f.getNode(j);
				faceString+=" "+n.getId();
			}
			printstream.println(faceString);
		}
		try {
			printstream.close();
			fileOutputStream.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void keyPressed() {
		println("keyPressed");
		doSave = true;
	}
}
